﻿using System.Text;
using KinonekoSoftware.CodariaDB.Documents.DataParts;
using KinonekoSoftware.CodariaDB.Documents.Index;
using KinonekoSoftware.CodariaDB.Documents.Keywords;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using JsonReader = Newtonsoft.Json.JsonReader;
using JsonSerializer = Newtonsoft.Json.JsonSerializer;
using JsonWriter = Newtonsoft.Json.JsonWriter;

namespace KinonekoSoftware.CodariaDB.IO.V4
{
    internal class DataPartConverter : JsonConverter<DataPart>
    {
        public override void WriteJson(JsonWriter writer, DataPart value, JsonSerializer serializer)
        {
        }

        public override DataPart ReadJson(JsonReader reader, Type objectType, DataPart existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            var jsonObject = JObject.Load(reader);

            if (jsonObject.TryGetValue("$type", out var type))
            {
                var objectType2 = Type.GetType(type.Value<string>());
                var instance    = Activator.CreateInstance(objectType2);
                serializer.Populate(jsonObject.CreateReader(), instance);

                return instance as DataPart;
            }


            return new Part_Classes();
        }
    }

    internal static class InstanceBinaryFormatterStatic
    {

        #region String

        public static string GetString(this BinaryReader reader)
        {
            var pos    = reader.BaseStream.Position;
            var length = reader.BaseStream.Length;
            var offset = length - pos;

            if (offset < 4)
            {
                return string.Empty;
            }

            var len = reader.ReadInt32();

            if (len == 0)
            {
                return string.Empty;
            }

            if (pos + len > length)
            {
                return string.Empty;
            }

            var bytes = reader.ReadBytes(len);
            return Encoding.UTF8.GetString(bytes);
        }

        public static void SetString(this BinaryWriter writer, string text)
        {
            var len = 0;

            if (string.IsNullOrEmpty(text))
            {
                writer.Write(len);
                return;
            }


            var bytes = Encoding.UTF8.GetBytes(text);
            len = bytes.Length;
            writer.Write(len);
            writer.Write(bytes);
        }

        #endregion

        #region DateTime

        public static DateTime GetDateTime(this BinaryReader reader)
        {
            var pos    = reader.BaseStream.Position;
            var length = reader.BaseStream.Length;
            var offset = length - pos;

            if (offset < 8)
            {
                return DateTime.Now;
            }

            var ticks = reader.ReadInt64();

            return new DateTime(ticks);
        }

        public static void SetDateTime(this BinaryWriter writer, DateTime dt)
        {
            writer.Write(dt.Ticks);
        }

        #endregion

        #region Image

        public static async Task GetImage(this BinaryReader reader, string id, GravatarSystem gravatar)
        {
            var len   = reader.ReadInt32();
            var bytes = reader.ReadBytes(len);
            await gravatar.WriteAsync(id, bytes);
        }

        public static async Task SetImage(this BinaryWriter writer, string id, GravatarSystem gravatar)
        {
            await using (var stream = await gravatar.OpenAsync(id))
            {

                var len   = (int)stream.Length;
                var bytes = new byte[len];
                var r     = await stream.ReadAsync(bytes, 0, len);

                Debug.Assert(r == len);
                writer.Write(len);
                writer.Write(bytes);
            }
        }


        public static async Task GetImage(this BinaryReader reader, string id, ImageSystem gravatar)
        {
            var len   = reader.ReadInt32();
            var bytes = reader.ReadBytes(len);
            await gravatar.WriteAsync(id, bytes);
        }

        public static async Task SetImage(this BinaryWriter writer, string id, ImageSystem gravatar)
        {
            await using (var stream = gravatar.Open(id))
            {

                var len   = (int)stream.Length;
                var bytes = new byte[len];
                var r     = await stream.ReadAsync(bytes, 0, len);

                Debug.Assert(r == len);
                writer.Write(len);
                writer.Write(bytes);
            }
        }

        #endregion

        #region Bin

        public static void GetPart(this BinaryReader reader, PartEngine engine)
        {
            var payload  = reader.GetString();
            var document = LiteDB.JsonSerializer.Deserialize(payload).AsDocument;
            var part     = BsonMapper.Global.Deserialize<DataPart>(document);

            if (part is null)
            {
                return;
            }

            if (engine.Has(part.Id, part.DocumentID))
            {
                engine.UpdatePart(part);
            }
            else
            {
                engine.AddPart(part);
            }
        }

        public static void SetPart(this BinaryWriter writer, DataPart part)
        {
            var document = BsonMapper.Global.ToDocument(part);
            var payload  = document.ToString();

            writer.SetString(payload);
        }

        #endregion
    }

    public struct InstanceBinaryFormatterContext
    {
        public PartEngine          PartEngine          { get; init; }
        public KeywordEngine       KeywordEngine       { get; init; }
        public GravatarSystem      GravatarSysytem     { get; init; }
        public ImageSystem         ImageSystem         { get; init; }
        public InstanceCacheEngine InstanceCacheEngine { get; init; }
    }

    public class InstanceBinaryFormatter
    {
        private const int CurrentVersion = 1;

        public static async Task<Result<InstanceCache>> ImportAsync(InstanceBinaryFormatterContext context, string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                return Result<InstanceCache>.Failed("文件名不存在");
            }

            try
            {
                await using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite))
                {
                    using (var reader = new BinaryReader(fs))
                    {
                        var ver = reader.ReadInt32();

                        return ver switch
                        {
                            _ => await ImportAsync_V1(context, reader),
                        };
                    }
                }

            }
            catch(Exception e)
            {
                return Result<InstanceCache>.Failed(e.Message);
            }
        }

        private static void WriterCache(BinaryWriter writer, InstanceCache target)
        {
            //
            // 导出Cache
            writer.SetString(target.Id);
            writer.SetString(target.Avatar);
            writer.SetString(target.Name);
            writer.SetString(target.Intro);
            writer.SetString(target.ParentID);
            writer.SetDateTime(target.TimeOfCreated);
            writer.SetDateTime(target.TimeOfModified);
            writer.Write((int)target.Type);
            writer.Write(target.ExtraType);
            writer.Write(target.Index);
            writer.Write(target.MaxIndex);
            writer.Write(target.MinIndex);
            writer.Write(target.Depth);
        }

        private static InstanceCache ReadCache(BinaryReader reader)
        {
            var id        = reader.GetString();
            var avatar    = reader.GetString();
            var name      = reader.GetString();
            var intro     = reader.GetString();
            var parent_id = reader.GetString();
            var toc       = reader.GetDateTime();
            var tom       = reader.GetDateTime();
            var type      = (InstanceType)reader.ReadInt32();
            var extraType = reader.ReadInt32();
            var index     = reader.ReadInt32();
            var maxIndex  = reader.ReadInt32();
            var minIndex  = reader.ReadInt32();
            var depth     = reader.ReadInt32();

            return new InstanceCache
            {
                Id             = id,
                Name           = name,
                Intro          = intro,
                ParentID       = parent_id,
                TimeOfCreated  = toc,
                TimeOfModified = tom,
                Type           = type,
                ExtraType      = extraType,
                Index          = index,
                MaxIndex       = maxIndex,
                MinIndex       = minIndex,
                Depth          = depth,
                Avatar         = avatar,
            };
        }

        public static async Task<Result> ExportAsync(InstanceBinaryFormatterContext context, InstanceCache target, string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                return Result.Failed("文件名不存在");
            }

            try
            {
                await using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite))
                {
                    await using (var writer = new BinaryWriter(fs))
                    {
                        //
                        // 当前版本
                        writer.Write(CurrentVersion);

                        //
                        // 导出Cache
                        WriterCache(writer, target);

                        //
                        // 导出头像
                        if (!string.IsNullOrEmpty(target.Avatar))
                        {
                            await writer.SetImage(target.Avatar, context.GravatarSysytem);
                        }

                        //
                        // 导出封面
                        if (!string.IsNullOrEmpty(target.Cover))
                        {
                            await writer.SetImage(target.Cover, context.ImageSystem);
                        }

                        var parts = context.PartEngine
                                           .Get(target.Id)
                                           .ToArray();

                        //
                        // 写入Part的数量
                        writer.Write(parts.Length);

                        foreach (var part in parts)
                        {
                            //
                            //
                            writer.SetPart(part);
                        }
                    }
                }

                //
                //
                return Result.Success;
            }
            catch(Exception e)
            {
                return Result.Failed(e.Message);
            }
        }

        #region V1

        private static async Task<Result<InstanceCache>> ImportAsync_V1(InstanceBinaryFormatterContext context, BinaryReader reader)
        {
            //
            // 导出Cache
            var target = ReadCache(reader);

            //
            // 导出头像
            if (!string.IsNullOrEmpty(target.Avatar))
            {
                await reader.GetImage(target.Avatar, context.GravatarSysytem);
            }

            //
            // 导出封面
            if (!string.IsNullOrEmpty(target.Cover))
            {
                await reader.GetImage(target.Cover, context.ImageSystem);
            }

            var partCount = reader.ReadInt32();


            if (partCount is < 0 or > 64)
            {
                return Result<InstanceCache>.Failed($"部件数量太多, {partCount}");
            }


            for (var i = 0; i < partCount; i++)
            {
                reader.GetPart(context.PartEngine);
            }

            var engine = context.InstanceCacheEngine;

            if (engine.Has(target.Id))
            {
                engine.UpdateCache(target);
            }
            else
            {
                engine.AddCache(target);
            }


            //
            //
            return Result<InstanceCache>.Success(target);
        }

        #endregion
    }
}